/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2009 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <SDL.h>
#include <cell/pad.h>
#include "SDL_cellvideo.h"
#include "input/keydata.h"
#include "input/SDL_cellpadmap.h"

#include "SDL_celloverimg.h"

struct							RCEntry
{
	char						Line[1024];
	char*						Name;
	char*						Value;

	struct RCEntry*				Next;
};

#ifdef CELL_SHADER_SUPPORT
char shader_file_name[1024];
#endif

struct RCEntry*					ReadRCFile							(const char* aFileName)
{
	struct RCEntry* output = 0;
	struct RCEntry* current = 0;
	FILE* rcfile;

	rcfile = fopen(aFileName, "r");

	if(rcfile)
	{
		output = malloc(sizeof(struct RCEntry));
		current = output;

		while(!feof(rcfile))
		{
			memset(current, 0, sizeof(struct RCEntry));
			if(fgets(current->Line, 1024, rcfile) && strchr(current->Line, '='))
			{
				/* Kill some characters */
				if(strrchr(current->Line, '\n'))	*strrchr(current->Line, '\n') = 0;
				if(strrchr(current->Line, '\r'))	*strrchr(current->Line, '\r') = 0;
				if(strrchr(current->Line, '#'))		*strrchr(current->Line, '#') = 0;

				current->Name = strtok(current->Line, " =");
				current->Value = strtok(0, " =");

				current->Next = malloc(sizeof(struct RCEntry));
				current = current->Next;
			}
		}

		memset(current, 0, sizeof(struct RCEntry));
		fclose(rcfile);
	}

	return output;
}

void							FreeRCFile							(struct RCEntry* aRC)
{
	struct RCEntry* Next = 0;

	if(aRC)
	{
		Next = aRC->Next;
		free(aRC);
	}
	else
	{
		return;
	}

	FreeRCFile(Next);	
}

const char*						FindRCValue							(struct RCEntry* aRC, const char* aValue)
{
	struct RCEntry* current = aRC;

	while(current)
	{
		if((current->Name && current->Value) && strcasecmp(aValue, current->Name) == 0)
		{
			return current->Value;
		}

		current = current->Next;
	}

	return 0;
}

uint32_t						RCFileSet;
char							RCFile[512];

#ifndef CELL_NO_SOFT_INPUT
static const char*				ButtonNames[] = {"btn_select", "btn_l3", "btn_r3", "btn_start", "btn_up", "btn_right", "btn_down", "btn_left", "btn_l2", "btn_r2",
											     "btn_l1", "btl_r1", "btn_triangle", "btn_circle", "btn_cross", "btn_square"};

#define	v(a) {#a, HID_##a}

static struct
{
	const char*					Name;
	enum HID_ID					Value;
}	ButtonValues[] = 
{
	v(NOEVENT), v(OVERRUN), v(POSTFAIL), v(UNDEFINEDERROR),

	//Letters
	v(a), v(b), v(c), v(d), v(e), v(f), v(g), v(h), v(i), v(j), v(k), v(l), v(m), 
	v(n), v(o), v(p), v(q), v(r), v(s), v(t), v(u), v(v), v(w), v(x), v(y), v(z),

	//Numbers
	v(1), v(2), v(3), v(4), v(5), v(6), v(7), v(8), v(9), v(0),

	v(RETURN), v(ESCAPE), v(BACKSPACE), v(TAB), v(SPACE), v(MINUS), v(PLUS),
	v(LEFTBRACKET), v(RIGHTBRACKET), v(BACKSLASH), v(EUROPE1_), v(SEMICOLON),
	v(QUOTE), v(BACKQUOTE), v(COMMA), v(PERIOD), v(SLASH), v(CAPSLOCK),
	v(F1), v(F2), v(F3), v(F4), v(F5), v(F6), v(F7), v(F8), v(F9), v(F10), v(F11), v(F12),
	v(PRINT), v(SCROLLOCK), v(PAUSE), v(INSERT), v(HOME), v(PAGEUP), v(DELETE), v(END),
	v(PAGEDOWN), v(RIGHT), v(LEFT), v(DOWN), v(UP), v(NUMLOCK),
	v(KP_DIVIDE), v(KP_MULTIPLY), v(KP_MINUS), v(KP_PLUS), v(KP_ENTER),
	v(KP1), v(KP2), v(KP3), v(KP4), v(KP5), v(KP6), v(KP7), v(KP8), v(KP9), v(KP0),
	v(KP_PERIOD), v(EUROPE2_), v(APP), v(KEYBOARDPOWER), v(KP_EQUALS),
	v(F13), v(F14), v(F15), v(F16_), v(F17_), v(F18_), v(F19_), v(F20_), v(F21_), v(F22_), v(F23_), v(F24_),
	v(EXECUTE_), v(HELP), v(MENU), v(SELECT_), v(STOP_), v(AGAIN_), v(UNDO_), v(CUT_),
	v(COPY_), v(PASTE_), v(FIND_), v(MUTE_), v(VOLUP_), v(VOLDN_), v(CAPSLOCK_),
	v(NUMLOCK_), v(SCROLLOCK_), v(BRAZIL_KP_PERIOD_), v(KBEQUALSIGN_), v(INTL1_), v(INTL2_),
	v(INTL3_), v(INTL4_), v(INTL5_),

	//Mouse: Not really hid but who cares?
	v(LEFTBUTTON), v(MIDDLEBUTTON), v(RIGHTBUTTON)
};
#undef v

/** BUTTONS **/
static uint32_t					MapButton						(const char* aName, const char* aValue)
{
	if(aValue)
	{
		/* If aValue is a number */
		enum HID_ID id = (enum HID_ID)atoi(aValue);

		/* If aValue wasn't a number, try looking for it as a string */
		if(!id)
		{
			for(int i = 0; i != sizeof(ButtonValues) / sizeof(ButtonValues[0]); i ++)
			{
				if(strcasecmp(aValue, ButtonValues[i].Name) == 0)
				{
					id = ButtonValues[i].Value;
				}
			}
		}

		/* Find aName as a button and map it */
		for(int i = 0; i != NUM_BUTTONS; i ++)
		{
			if(strcasecmp(aName, ButtonNames[i]) == 0)
			{
				CELL_PAD_KEYBOARD_Map(i, id);
				return 1;
			}
		}
	}

	return 0;
}

static void						DumpButton						(uint32_t aIndex, FILE* aOutput)
{
	if(aIndex < NUM_BUTTONS)
	{
		enum HID_ID id = CELL_PAD_KEYBOARD_GetMap(aIndex);

		/* Try to print it as a string */
		for(int i = 0; i != sizeof(ButtonValues) / sizeof(ButtonValues[0]); i ++)
		{
			if(ButtonValues[i].Value == id)
			{
				fprintf(aOutput, "%s=%s\n", ButtonNames[aIndex], ButtonValues[i].Name);
				return;
			}
		}

		/* Print it as a number */
		fprintf(aOutput, "%s=%d\n", ButtonNames[aIndex], id);
	}
}
#endif

/** UNDERSCAN **/
static uint32_t					GotUnderscanValues;
static int32_t					UnderscanX, UnderscanY;

static uint32_t					MapUnderscan					(const char* aName, const char* aValue)
{
	if(strcasecmp(aName, "screen_underscanx") == 0 && aValue)
	{
		GotUnderscanValues = 1;
		UnderscanX = atoi(aValue);
		return 1;
	}

	if(strcasecmp(aName, "screen_underscany") == 0 && aValue)
	{
		GotUnderscanValues = 1;
		UnderscanY = atoi(aValue);
		return 1;
	}

	return 0;
}

static CELL_OverlayImage overimage;
static CELL_OverlayImage helpimage;

static void						CalculateUnderscan				()
{
	/* Setup libpad for our needs */
	CellPadData data;
	uint32_t buttons = 0;

	/* Get the current button state */
	if(CELL_OK == cellPadGetData(0, &data) && data.len >= 8)
	{
		buttons = data.button[2] | (data.button[3] << 8);
	}

	/* If we didn't get anything from the rcfile, or the user is holding select */
	if(!GotUnderscanValues || buttons & 1)
	{
		UnderscanX = 0;
		UnderscanY = 0;

		/* Get the screen size */
		uint32_t width, height;
		psglGetDeviceDimensions(psglGetCurrentDevice(), &width, &height);

		/* Generate image */
		uint32_t* img = malloc((width / 10) * (height / 10) * 4);
		for(int i = 0; i != height / 10; i ++)
		{
			for(int j = 0; j != width / 10; j ++)
			{
				img[i * (width / 10) + j] = (i == 0 || j == 0 || i == (height / 10 - 1) || j == (width / 10 - 1)) ? 0xFF0000FF : 0xFF000000;
			}
		}

		CELL_IMAGE_Load(&overimage, 0, 0, width, height, img, width / 10, height / 10);

		free(img);

		CELL_IMAGE_Load(&helpimage, width / 2 - OVER_WIDTH / 2, height / 2 - OVER_HEIGHT / 2, OVER_WIDTH, OVER_HEIGHT, OverscanHelpImage, OVER_WIDTH, OVER_HEIGHT);

		/* Setup drawing */
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrthof(0, width, height, 0, -1, 1);

		/* Loop */
		while(1)
		{
			/* Draw screen */
			float widthP = ((float)width) * (((float)(UnderscanX)) / 100.0f);
			float heightP = ((float)height) * (((float)(UnderscanY)) / 100.0f);
			glViewport(widthP, heightP, width - widthP * 2, height - heightP * 2);
			glClear(GL_COLOR_BUFFER_BIT);
			CELL_IMAGE_Draw(&overimage);
			CELL_IMAGE_Draw(&helpimage);
			psglSwap();

			/* Update buttons */
			if(CELL_OK == cellPadGetData(0, &data) && data.len >= 8)
			{
				buttons = data.button[2] | (data.button[3] << 8);
			}

			/* Update state */
			if(buttons & 0x4000)		break;
			if(buttons & 0x10)			UnderscanY --;
			if(buttons & 0x40)			UnderscanY ++;
			if(buttons & 0x20)			UnderscanX --;
			if(buttons & 0x80)			UnderscanX ++;

			UnderscanX = (UnderscanX < -5) ? -5 : UnderscanX;
			UnderscanY = (UnderscanY < -5) ? -5 : UnderscanY;
			UnderscanX = (UnderscanX > 25) ? 25 : UnderscanX;
			UnderscanY = (UnderscanY > 25) ? 25 : UnderscanY;

			SDL_Delay(50);
		}

		/* Release the image */
//		CELL_IMAGE_Free(&overimage);
		CELL_IMAGE_Free(&helpimage);
	}

	CELL_PSGL_SetUnderscan(UnderscanX, UnderscanY);
}

#ifdef CELL_SHADER_SUPPORT
/** SHADERS **/
static uint32_t					MapShader						(const char* aName, const char* aValue)
{
	if(strcasecmp(aName, "screen_shader") == 0 && aValue)
	{
		struct RCEntry* preset = ReadRCFile(aValue);

		if(preset)
		{
			strcpy(shader_file_name, aValue);//HACK:

			if(FindRCValue(preset, "smooth"))
			{
				CELL_Video.Shader[0].Smooth = atoi(FindRCValue(preset, "smooth"));
			}

			if(FindRCValue(preset, "smooth2"))
			{
				CELL_Video.Shader[1].Smooth = atoi(FindRCValue(preset, "smooth"));
			}

			if(FindRCValue(preset, "PS3CurrentShader"))
			{
				CELL_SHADE_Load(FindRCValue(preset, "PS3CurrentShader"), 0);
			}

			if(FindRCValue(preset, "PS3CurrentShader2"))
			{
				CELL_SHADE_Load(FindRCValue(preset, "PS3CurrentShader2"), 1);
			}

			if(FindRCValue(preset, "ScaleFactor"))
			{
				CELL_Video.Shader[0].ScaleFactor = atoi(aValue);
			}

			if(CELL_Video.Shader[0].ScaleFactor == 0)
			{
				CELL_Video.Shader[0].ScaleFactor = 1;
			}

			/* TODO: Scaling */

			FreeRCFile(preset);
		}

		return 1;
	}

	return 0;
}
#endif

void							CELL_RC_SetFile					(const char* aPath)
{
	RCFileSet = 1;
	strncpy(RCFile, aPath, 512);
}

void							CELL_RC_GetData					()
{
	/* First map some default keys, map em later and they overwrite the ones loaded here */
#ifndef CELL_NO_SOFT_INPUT
	CELL_PAD_KEYBOARD_Map(4, HID_UP);
	CELL_PAD_KEYBOARD_Map(6, HID_DOWN);
	CELL_PAD_KEYBOARD_Map(7, HID_LEFT);
	CELL_PAD_KEYBOARD_Map(5, HID_RIGHT);
	CELL_PAD_KEYBOARD_Map(0, HID_TAB);
	CELL_PAD_KEYBOARD_Map(3, HID_RETURN);
	CELL_PAD_KEYBOARD_Map(9, HID_ESCAPE);
	CELL_PAD_KEYBOARD_Map(14, HID_LEFTBUTTON);
	CELL_PAD_KEYBOARD_Map(15, HID_RIGHTBUTTON);
#endif

	/* Read any rc file */
	if(RCFileSet && RCFile[0] == '/')
	{
		struct RCEntry* rcfile = ReadRCFile(RCFile);

#ifndef CELL_NO_SOFT_INPUT
		for(int i = 0; i != 16; i ++)
		{
			MapButton(ButtonNames[i], FindRCValue(rcfile, ButtonNames[i]));
		}
#endif

		MapUnderscan("screen_underscanx", FindRCValue(rcfile, "screen_underscanx"));
		MapUnderscan("screen_underscanY", FindRCValue(rcfile, "screen_underscany"));
#ifdef CELL_SHADER_SUPPORT
		MapShader("screen_shader", FindRCValue(rcfile, "screen_shader"));
#endif

		FreeRCFile(rcfile);
	}

	CalculateUnderscan();
}

void							CELL_RC_DumpData				()
{
	if(RCFileSet && RCFile[0] == '/')	/* If we have an rc file */
	{
		FILE* rcfile = fopen(RCFile, "w");
		if(rcfile)			/* And can open it */
		{
			/* Write buttons */
#ifndef CELL_NO_SOFT_INPUT
			CELL_PAD_SetFixedMap(0);
			for(int i = 0; i != NUM_BUTTONS; i ++)
			{
				DumpButton(i, rcfile);
			}
#endif

			/* Write underscan */
			int32_t underX, underY;
			CELL_PSGL_GetUnderscan(&underX, &underY);
			fprintf(rcfile, "screen_underscanx=%d\n", underX);
			fprintf(rcfile, "screen_underscany=%d\n", underY);

			/* Write shader */
#ifdef CELL_SHADER_SUPPORT
			if(strlen(shader_file_name))
			{
				fprintf(rcfile, "screen_shader=%s\n", shader_file_name);
			}
#endif

			fclose(rcfile);
		}
	}
}


